use crate::addrspace::{PfnAddr, PhysAddr};

use super::{Memblock, MemblockRegion};

impl Memblock {
    /// 设置当前的 bottom_up, 默认自顶向下分配
    ///
    /// 为真则自底向上分配内存, 反之自顶向下分配内存
    pub fn set_bottom_up(&mut self, enable: bool) {
        self.bottom_up = enable;
    }

    /// 获取当前的 bottom_up 信息
    ///
    /// 为真则自底向上分配内存, 反之自顶向下分配内存
    pub fn is_bottom_up(&self) -> bool {
        self.bottom_up
    }

    /// 设置当前的 current_limit
    ///
    /// 限制能够分配的最大地址范围
    #[inline]
    pub fn set_current_limit(&mut self, limit: PhysAddr) {
        self.current_limit = limit;
    }

    /// 获取当前的 current_limit
    ///
    /// 返回能够分配的最大地址范围
    #[inline]
    pub fn current_limit(&self) -> PhysAddr {
        self.current_limit
    }

    /// 设置当前的 bottom_limit
    ///
    /// 当自顶向下分配时, 限制能够分配的最小地址范围
    pub fn set_bottom_limit(&mut self, limit: PhysAddr) {
        self.bottom_limit = limit;
    }

    /// 获取当前的 bottom_limit
    #[inline]
    pub fn bottom_limit(&self) -> PhysAddr {
        self.bottom_limit
    }

    /// 返回总的可用内存大小
    #[inline]
    pub fn total_size(&self) -> usize {
        self.memory.total_size
    }

    /// 返回总的预留内存大小
    #[inline]
    pub fn reserved_size(&self) -> usize {
        self.reserved.total_size
    }

    /// 从指定的最大 Pfn 并对齐 PAGE_SIZE 返回总的可用内存大小
    pub fn mem_size(&mut self, limit_pfn: PfnAddr) -> usize {
        let mut pages = 0;

        for rgn in self.iter_mut_each_mem() {
            let start_pfn = memblock_region_memory_base_pfn(rgn).min(limit_pfn);
            let end_pfn = memblock_region_memory_end_pfn(rgn).min(limit_pfn);
            pages += (end_pfn - start_pfn).value();
        }

        PfnAddr::from(pages).pfn_to_phys().value()
    }

    /// 返回可用内存的起始地址
    #[allow(non_snake_case)]
    #[inline]
    pub fn start_of_DRAM(&self) -> PhysAddr {
        self.memory.regions[0].base
    }

    /// 返回可用内存的结束地址
    #[allow(non_snake_case)]
    #[inline]
    pub fn end_of_DRAM(&self) -> PhysAddr {
        let idx = self.memory.cnt - 1;
        let rgn = &self.memory.regions[idx];

        rgn.base + rgn.size
    }
}

#[inline]
fn memblock_region_memory_base_pfn(reg: &MemblockRegion) -> PfnAddr {
    reg.base.phys_to_pfn_up()
}

#[inline]
fn memblock_region_memory_end_pfn(reg: &MemblockRegion) -> PfnAddr {
    (reg.base + reg.size).phys_to_pfn_down()
}
